package bea.cliente.core;

import com.jme3.app.Application;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.input.FlyByCamera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial.CullHint;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeSystem;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class Core extends Application {

    // NODO principal da aplicação
    protected Node rootNode = new Node("Root:Node");
    // NODO da interface
    protected Node guiNode = new Node("Gui:Node");
    // variaveis do "FPS" stats
    protected BitmapText fpsText;
    protected BitmapFont guiFont;
    // camera em primeira pessoa
    protected FlyByCamera flyCam;
    // variavel de controle da tela de configurações
    protected boolean showSettings = false;
    // arquivo com as configurações salvas pelo usuario
    protected GameSettings gameSettings;

    /*
     * Construtor padrão
     */
    public Core() {
    }

    @Override
    public void start() {
        // carrega as configurações do arquivo e configura uma nova "AppSettings"
        initSettings();
        // grava o AppSettings na memoria para futuros acessos
        Memory.put(Memory.GAME_SETTINGS, settings);
        // grava o arquivo de configuração na memoria para futuros acessos
        Memory.put(Memory.GAME_PROPERTIES_FILE, gameSettings);

        // mostra ou não a tela de configurações
        if (showSettings) {
            if (!JmeSystem.showSettingsDialog(settings, false)) {
                return;
            }
        }
        // inicia o jogo com as configurações carregadas
        super.start();
    }

    /**
     * 
     * @return A camera em Terceira Pessoa
     */
    public FlyByCamera getFlyByCamera() {
        return flyCam;
    }

    /**
     * 
     * @return O nodo da interface
     */
    public Node getGuiNode() {
        return guiNode;
    }

    /**
     * 
     * @return O nodo raiz da aplicação caso este tenha sido inicializado
     */
    public Node getRootNode() {
        return rootNode;
    }

    /**
     * 
     * @return Variavel de controle de mostrar a 'settings screen'
     */
    public boolean isShowSettings() {
        return showSettings;
    }

    /**
     * Configura a variavel de controle da 'Settings screen'
     */
    public void setShowSettings(boolean showSettings) {
        this.showSettings = showSettings;
    }

    /**
     * carrega o texto FPS para debug
     */
    public void loadFPSText() {
        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
        fpsText = new BitmapText(guiFont, false);
        fpsText.setSize(guiFont.getCharSet().getRenderedSize());
        fpsText.setLocalTranslation(settings.getWidth() - 70, settings.getHeight() - fpsText.getLineHeight(), 0);
        fpsText.setText("FPS:");
        guiNode.attachChild(fpsText);
    }

    @Override
    public void initialize() {
        super.initialize();

        guiNode.setQueueBucket(Bucket.Gui);
        guiNode.setCullHint(CullHint.Never);
        loadFPSText();
        viewPort.attachScene(rootNode);
        guiViewPort.attachScene(guiNode);

        // se tem inputManager configura a camera em terceira pessoa
        if (inputManager != null) {
            flyCam = new FlyByCamera(cam);
            flyCam.setMoveSpeed(1f);
            flyCam.registerWithInput(inputManager);
        }
        // invoca o método da classe herdeira "Game"
        gameInit();
    }

    @Override
    public void update() {
        super.update();

        float tpf = timer.getTimePerFrame() * speed;

        int fps = (int) timer.getFrameRate();
        fpsText.setText("FPS: " + fps);

        // chama método de update do jogo
        gameUpdate(tpf);

        // update states
        stateManager.update(tpf);

        // atualiza os nodos
        rootNode.updateLogicalState(tpf);
        guiNode.updateLogicalState(tpf);
        rootNode.updateGeometricState();
        guiNode.updateGeometricState();

        // render states
        stateManager.render(renderManager);

        renderManager.render(tpf, true);

        // chama o render do jogo
        gameRender(renderManager);
    }

    /**
     * Método herdado e implementado pela classe <code>Game</code>
     * @param tpf 
     */
    public abstract void gameInit();

    /**
     * Método herdado e implementado pela classe <code>Game</code>
     * @param tpf 
     */
    public abstract void gameUpdate(float tpf);

    public abstract void gameRender(RenderManager rm);

    private void initSettings() {
        // carrega o arquivo de propriedades
        gameSettings = new GameSettings(this, settings);
        FileInputStream inputStream;
        // carrega o arquivo
        try {
            inputStream = new FileInputStream(new File("assets/Configuration/configuration.properties"));
            gameSettings.load(inputStream);
        } catch (IOException ex) {
            Logger.getLogger(Core.class.getName()).log(Level.SEVERE, "Configurations file not found!", ex);
            System.exit(1);
        }
        // carrega as informações
        try {
            AppSettings loadedSettings = new AppSettings(true);
            // FRAME RATE
            loadedSettings.setFrameRate(Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_FRAMERATE)));
            // TAMANHO
            loadedSettings.setResolution(Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_WIDTH)), Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_HEIGHT)));
            // ANTIALAINSING
            loadedSettings.setSamples(Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_ANTIALAISING)));
            // FULLSCREEN ?
            loadedSettings.setFullscreen(Boolean.parseBoolean(gameSettings.getProperty(GameSettings.KEY_FULLSCREEN)));
            // FREQUENCIA
            loadedSettings.setFrequency(Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_FREQUENCY)));
            // RENDERER
            loadedSettings.setRenderer(gameSettings.getProperty(GameSettings.KEY_RENDERER));
            // HAS SHADOW ?
            loadedSettings.putBoolean(GameSettings.KEY_SHADOW, Boolean.parseBoolean(gameSettings.getProperty(GameSettings.KEY_SHADOW)));
            // SHADOW TYPE
            loadedSettings.putInteger(GameSettings.KEY_SHADOW_TYPE, Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_SHADOW_TYPE)));
            // SHADOW LEVEL
            loadedSettings.putInteger(GameSettings.KEY_SHADOW_LEVEL, Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_SHADOW_LEVEL)));
            // PROCESSOR CORES
            loadedSettings.putInteger(GameSettings.KEY_PROCESSOR_CORES, Integer.parseInt(gameSettings.getProperty(GameSettings.KEY_PROCESSOR_CORES)));
            // muda a AppSettings para a 
            setSettings(loadedSettings);
            gameSettings.setSettings(loadedSettings);
        } finally{}
    }
}
